#include "pm.h"
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdlib.h>
#include <assert.h>

void create_pm(char *fname, long size, struct local_pm *pm){
  int fd = open(fname, O_CREAT | O_RDWR, 0600);
  void *addr;
  assert(fd > 0);
  pm->fd = fd;
  addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd ,0);
  assert(addr);
  pm->addr = addr;
  pm->size = size;
}
void destory_pm(struct local_pm *pm){
  munmap(pm->addr, pm->size);
  close(pm->fd);
  memset(pm, 0, sizeof(struct local_pm));
}

struct pm_super *init_pm(struct local_pm *pm){
  BUILD_BUG_ON(sizeof(struct node_pos) != 8);
  BUILD_BUG_ON(sizeof(struct node_data)!= 64);
  long block_num, reserve_num;
  long super_bfn;
  struct block_data *bd;
  void *start;
  struct pm_super *super;
  int i, super_block_num;

  p_info("start init pm\n");
  start = pm->addr;
  bd = (struct block_data *)pm->addr;
  block_num = pm->size / BLOCK_SIZE;
  assert(block_num);
  reserve_num = (block_num * sizeof(struct block_data) + BLOCK_SIZE - 1) / BLOCK_SIZE;
  p_info("reserve num %ld\n", reserve_num);
  memset(pm->addr, 0, pm->size);
  for(i = 0; i < reserve_num; i++){
    bd[i].type = BLOCK_TYPE_RESERVE;
  }
  super_bfn = reserve_num;
  p_info("super bfn %ld\n", super_bfn);
  bd[super_bfn].type = BLOCK_TYPE_SUPER;
  super = (struct pm_super *)bfn2addr(super_bfn, start);
  super->magic = SUPER_MAGIC;
  super->hash = (((unsigned long)rand()) << 32) + rand();
  super_block_num = (sizeof(struct pm_super) + BLOCK_SIZE - 1)/BLOCK_SIZE;
  for(i = 0; i < super_block_num; i++){
    bd[super_bfn + i].type = BLOCK_TYPE_RESERVE;
  }
  return super;
}

struct pm_allocator *init_pm_allocator(struct local_pm *pm){
  struct pm_allocator *alr;
  struct block_data *bd;
  long node_per_block;
  int block_num, i, j;
  
  alr = new struct pm_allocator;
  assert(alr);
  block_num = pm->size / BLOCK_SIZE;
  node_per_block = BLOCK_SIZE / sizeof(node_data);
  bd = (struct block_data *)pm->addr;

  for(i = 0; i < block_num; i++){
    if(bd[i].type == BLOCK_TYPE_FREE){
      alr->free_block.push_back(i);
    }else if(bd[i].type == BLOCK_TYPE_NODE){
      struct bitmap *bm = (struct bitmap *)bfn2addr(i, pm->addr);
      for(j = 0; j < node_per_block; j++){
        if(!getbit(bm, j)){
          alr->free_node.push_back({(unsigned long)i, (unsigned long)j});
        }
      }
    }
  }
  p_info("allocator init done, free block %ld, free node %ld\n", 
      alr->free_block.size(), alr->free_node.size());
  return alr;
}
